{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "0a263d22-1eeb-4819-9196-1db1befecd7d",
   "metadata": {},
   "source": [
    "# 检索增强生成（Retrieval Augmented Generation, RAG）第 2 部分：  \n",
    "> 构建一个结合用户交互记忆和多步骤检索的 RAG 应用程序。\n",
    "\n",
    "### 构建一个检索增强生成（RAG）应用程序：第 2 部分\n",
    "\n",
    "在许多问答（Q&A）应用中，我们希望用户能够进行**来回对话**。这意味着应用程序需要具备某种形式的“记忆”功能，记录过去的提问与回答，并将这些信息纳入当前的思考逻辑中。\n",
    "\n",
    "这是本系列教程的第二部分：\n",
    "\n",
    "- **第 1 部分**：介绍了 RAG 的基本概念，并演示了一个最小可行实现。\n",
    "- **第 2 部分（本指南）**：扩展了该实现，使其支持**对话式交互**和**多步骤检索流程**。\n",
    "\n",
    "在本部分中，我们将重点介绍如何加入**历史消息处理逻辑**，即管理聊天记录（chat history）。\n",
    "\n",
    "---\n",
    "\n",
    "我们将介绍两种主要实现方式：\n",
    "\n",
    "#### 1. 使用 Chains（链式调用）\n",
    "在这种方法中，最多只执行一次检索操作。它适用于结构较为固定的流程。\n",
    "\n",
    "#### 2. 使用 Agents（智能体）\n",
    "这种方法赋予语言模型（LLM）更大的自主权，使其可以根据需要执行**多个检索步骤**，从而更灵活地处理复杂问题。\n",
    "\n",
    "> 💡 **注意**：  \n",
    "本节所使用的方法依赖于现代聊天模型中的 **工具调用（tool calling）** 功能。  \n",
    "\n",
    "---\n",
    "\n",
    "### 数据源说明\n",
    "\n",
    "为了提供外部知识支持，我们将继续使用第 1 部分中使用的那篇博客文章 —— [**中小企业出海对接交流会**](https://gyxxh.tj.gov.cn/glllm/gabsycs/gxdtgh/202504/t20250408_6902492.html)。\n",
    "\n",
    "### 我们首先回顾一下在第 1 部分中构建的向量数据库（vector store）。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "a880bbe6-78b7-4056-95bd-e8e3f236f01a",
   "metadata": {},
   "outputs": [],
   "source": [
    "import getpass\n",
    "import os\n",
    "\n",
    "try:\n",
    "    # load environment variables from .env file (requires `python-dotenv`)\n",
    "    from dotenv import load_dotenv\n",
    "\n",
    "    _ = load_dotenv()\n",
    "except ImportError:\n",
    "    pass\n",
    "\n",
    "if not os.environ.get(\"DASHSCOPE_API_KEY\"):\n",
    "  os.environ[\"DASHSCOPE_API_KEY\"] = getpass.getpass(\"Enter API key for OpenAI: \")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "af2b6aa7-e0df-42ff-933a-b7c5f33ba891",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_community.chat_models.tongyi import ChatTongyi\n",
    "\n",
    "llm  = ChatTongyi(\n",
    "    streaming=True,\n",
    "    name=\"qwen-turbo\"\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "9265912d-3c45-4321-b3a8-065f4396b09c",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_community.embeddings.dashscope import DashScopeEmbeddings\n",
    "# 初始化 Qwen Embedding 模型\n",
    "embeddings = DashScopeEmbeddings(model=\"text-embedding-v1\")  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "7159eb33-4a79-47f5-bf4f-4cae066e7dda",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_core.vectorstores import InMemoryVectorStore\n",
    "\n",
    "vector_store = InMemoryVectorStore(embeddings)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "4e448c09-7a5e-4721-ae68-005d01e7998e",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "USER_AGENT environment variable not set, consider setting it to identify your requests.\n"
     ]
    }
   ],
   "source": [
    "import bs4\n",
    "from langchain import hub\n",
    "from langchain_community.document_loaders import WebBaseLoader\n",
    "from langchain_core.documents import Document\n",
    "from langchain_text_splitters import RecursiveCharacterTextSplitter\n",
    "from typing_extensions import List, TypedDict\n",
    "\n",
    "# Load and chunk contents of the blog\n",
    "loader = WebBaseLoader(\n",
    "    web_paths=(\"https://gyxxh.tj.gov.cn/glllm/gabsycs/gxdtgh/202504/t20250408_6902492.html\",),\n",
    "    bs_kwargs=dict(\n",
    "        parse_only=bs4.SoupStrainer(\n",
    "            class_=(\"view-wrap\")\n",
    "        )\n",
    "    ),\n",
    ")\n",
    "docs = loader.load()\n",
    "\n",
    "text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)\n",
    "all_splits = text_splitter.split_documents(docs)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "ce698cb7-74c2-4367-9571-f6db8b9179cc",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Index chunks\n",
    "_ = vector_store.add_documents(documents=all_splits)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "121075fe-4070-4f5e-82a8-94698737db9c",
   "metadata": {},
   "source": [
    "在 RAG 教程的第 1 部分中，我们将用户输入、检索到的上下文以及生成的回答分别作为状态中的独立键来表示。\n",
    "\n",
    "而在对话式应用场景中，使用**消息序列（sequence of messages）** 可以更自然地表达交互过程。除了用户的输入（User）和助手的回复（Assistant）之外，检索到的文档和其他中间结果也可以通过 **工具消息（ToolMessage）** 的形式插入到消息序列中。\n",
    "\n",
    "这促使我们使用一个**消息序列**来表示 RAG 应用的状态。具体来说，我们的消息结构将包括：\n",
    "\n",
    "- 用户输入：`HumanMessage`\n",
    "- 向量数据库查询：带有工具调用的 `AIMessage`\n",
    "- 检索到的文档：`ToolMessage`\n",
    "- 最终回复：`AIMessage`\n",
    "\n",
    "这种状态表示方式非常灵活，因此 LangGraph 提供了一个**内置版本**来简化这一流程，方便开发者直接使用。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "4114cc43-f405-4ac6-8ad3-d16abed9f47e",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langgraph.graph import MessagesState, StateGraph\n",
    "\n",
    "graph_builder = StateGraph(MessagesState)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9014967d-2dea-46f7-916b-f1f56de74e69",
   "metadata": {},
   "source": [
    "利用**工具调用（tool-calling）** 来与检索步骤进行交互还有一个额外的好处：即检索所使用的查询是由我们的语言模型生成的。这一点在**对话式场景**中尤为重要，因为用户的提问可能需要根据聊天历史进行上下文化理解（contextualization）。例如，考虑以下对话：\n",
    "\n",
    "---\n",
    "\n",
    "**用户**： “什么是任务分解（Task Decomposition）？”\n",
    "\n",
    "**AI**： “任务分解是指将复杂的任务拆分成更小、更简单的步骤，从而使智能体或模型更容易处理。”\n",
    "\n",
    "**用户**： “常见的实现方式有哪些？”\n",
    "\n",
    "---\n",
    "\n",
    "在这种情况下，模型可以自动生成一个检索查询，例如 `\"common approaches to task decomposition\"`（任务分解的常见方法）。而 **工具调用机制** 可以非常自然地支持这一行为。\n",
    "\n",
    "正如在 RAG 教程的“查询分析”部分所提到的那样，这种方式允许模型将用户的原始问题重写为更高效的搜索查询。同时，它也支持**无需检索步骤的直接回答**（例如，当用户发送的是通用问候语时）。\n",
    "\n",
    "---\n",
    "\n",
    "### 现在，让我们将检索步骤封装为一个工具："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "c5ac197e-663f-44a1-8a4a-66b3a16ff027",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_core.tools import tool\n",
    "\n",
    "\n",
    "@tool(response_format=\"content_and_artifact\")\n",
    "def retrieve(query: str):\n",
    "    \"\"\"Retrieve information related to a query.\"\"\"\n",
    "    retrieved_docs = vector_store.similarity_search(query, k=2)\n",
    "    serialized = \"\\n\\n\".join(\n",
    "        (f\"Source: {doc.metadata}\\n\" f\"Content: {doc.page_content}\")\n",
    "        for doc in retrieved_docs\n",
    "    )\n",
    "    return serialized, retrieved_docs"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "36054f4a-4b5c-42bf-8df3-d30fa89888af",
   "metadata": {},
   "source": [
    "我们的图（Graph）将包含三个节点：\n",
    "\n",
    "1. **处理用户输入的节点**：该节点会分析用户的提问，决定是为检索器生成一个查询语句，还是直接做出回应；\n",
    "2. **检索工具节点**：该节点负责执行实际的检索步骤；\n",
    "3. **生成最终回答的节点**：使用检索到的上下文信息，生成最终的自然语言回复。\n",
    "\n",
    "我们将在下方构建这三个节点。请注意，我们会使用 LangGraph 提供的另一个预构建组件 **`ToolNode`**，它能够自动执行工具调用，并将结果作为 `ToolMessage` 添加到当前的状态（State）中。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "06f53d48-4864-4148-9bca-786370feb0da",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_core.messages import SystemMessage\n",
    "from langgraph.prebuilt import ToolNode\n",
    "\n",
    "\n",
    "# Step 1: Generate an AIMessage that may include a tool-call to be sent.\n",
    "def query_or_respond(state: MessagesState):\n",
    "    \"\"\"Generate tool call for retrieval or respond.\"\"\"\n",
    "    llm_with_tools = llm.bind_tools([retrieve])\n",
    "    response = llm_with_tools.invoke(state[\"messages\"])\n",
    "    # MessagesState appends messages to state instead of overwriting\n",
    "    return {\"messages\": [response]}\n",
    "\n",
    "\n",
    "# Step 2: Execute the retrieval.\n",
    "tools = ToolNode([retrieve])\n",
    "\n",
    "\n",
    "# Step 3: Generate a response using the retrieved content.\n",
    "def generate(state: MessagesState):\n",
    "    \"\"\"Generate answer.\"\"\"\n",
    "    # Get generated ToolMessages\n",
    "    recent_tool_messages = []\n",
    "    for message in reversed(state[\"messages\"]):\n",
    "        if message.type == \"tool\":\n",
    "            recent_tool_messages.append(message)\n",
    "        else:\n",
    "            break\n",
    "    tool_messages = recent_tool_messages[::-1]\n",
    "\n",
    "    # Format into prompt\n",
    "    docs_content = \"\\n\\n\".join(doc.content for doc in tool_messages)\n",
    "    system_message_content = (\n",
    "        \"You are an assistant for question-answering tasks. \"\n",
    "        \"Use the following pieces of retrieved context to answer \"\n",
    "        \"the question. If you don't know the answer, say that you \"\n",
    "        \"don't know. Use three sentences maximum and keep the \"\n",
    "        \"answer concise.\"\n",
    "        \"\\n\\n\"\n",
    "        f\"{docs_content}\"\n",
    "    )\n",
    "    conversation_messages = [\n",
    "        message\n",
    "        for message in state[\"messages\"]\n",
    "        if message.type in (\"human\", \"system\")\n",
    "        or (message.type == \"ai\" and not message.tool_calls)\n",
    "    ]\n",
    "    prompt = [SystemMessage(system_message_content)] + conversation_messages\n",
    "\n",
    "    # Run\n",
    "    response = llm.invoke(prompt)\n",
    "    return {\"messages\": [response]}"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "801f5229-d8b6-4384-83b3-0770ba9b59c4",
   "metadata": {},
   "source": [
    "最后，我们将整个应用程序编译成一个**图对象（graph object）**。在本例中，我们只是将各个步骤按顺序连接起来形成一个流程。\n",
    "\n",
    "此外，我们还允许第一个步骤 `query_or_respond`（查询或回应）进行“短路”处理 —— 也就是说，如果它**没有生成工具调用（tool call）**，就可以直接向用户返回响应。\n",
    "\n",
    "这种设计使我们的应用程序能够更好地支持**对话式交互体验**，例如：  \n",
    "- 对不需要检索步骤的通用问候语（如“你好”、“谢谢”）做出直接回应；\n",
    "- 根据上下文判断是否需要执行检索，提升交互的灵活性和自然性。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "9726047a-fdd5-4e22-a84f-e0a3538ea32a",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langgraph.graph import END\n",
    "from langgraph.prebuilt import ToolNode, tools_condition\n",
    "\n",
    "graph_builder.add_node(query_or_respond)\n",
    "graph_builder.add_node(tools)\n",
    "graph_builder.add_node(generate)\n",
    "\n",
    "graph_builder.set_entry_point(\"query_or_respond\")\n",
    "graph_builder.add_conditional_edges(\n",
    "    \"query_or_respond\",\n",
    "    tools_condition,\n",
    "    {END: END, \"tools\": \"tools\"},\n",
    ")\n",
    "graph_builder.add_edge(\"tools\", \"generate\")\n",
    "graph_builder.add_edge(\"generate\", END)\n",
    "\n",
    "graph = graph_builder.compile()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "f119004c-4bc3-4a41-b92c-9bd54b77809a",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from IPython.display import Image, display\n",
    "\n",
    "display(Image(graph.get_graph().draw_mermaid_png()))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "7cda755b-73ab-4b3c-a34d-e3f3f242dd70",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "================================\u001b[1m Human Message \u001b[0m=================================\n",
      "\n",
      "你好\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "你好！有什么我可以帮助你的？\n"
     ]
    }
   ],
   "source": [
    "input_message = \"你好\"\n",
    "\n",
    "for step in graph.stream(\n",
    "    {\"messages\": [{\"role\": \"user\", \"content\": input_message}]},\n",
    "    stream_mode=\"values\",\n",
    "):\n",
    "    step[\"messages\"][-1].pretty_print()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "bf211f21-e75f-4d70-9dfe-137a91268e46",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "================================\u001b[1m Human Message \u001b[0m=================================\n",
      "\n",
      "有多少家企业参加活动?\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "Tool Calls:\n",
      "  retrieve (call_8dd0ac83ac544e6cb7261acall_8dd0ac83ac544e6cb7261acall_8dd0ac83ac544e6cb7261acall_8dd0ac83ac544e6cb7261a)\n",
      " Call ID: call_8dd0ac83ac544e6cb7261acall_8dd0ac83ac544e6cb7261acall_8dd0ac83ac544e6cb7261acall_8dd0ac83ac544e6cb7261a\n",
      "  Args:\n",
      "    query: 参加活动的企业数量\n",
      "=================================\u001b[1m Tool Message \u001b[0m=================================\n",
      "Name: retrieve\n",
      "\n",
      "Source: {'source': 'https://gyxxh.tj.gov.cn/glllm/gabsycs/gxdtgh/202504/t20250408_6902492.html'}\n",
      "Content: 来源：\n",
      "          天津市工业和信息化局\n",
      "        \n",
      "发布时间：\n",
      "          2025-04-08 09:30\n",
      "        \n",
      "\n",
      "\n",
      "为深入贯彻党的二十届三中全会精神，落实《工业和信息化部办公厅关于开展中小企业出海服务专项行动的通知》及《工业和信息化部等17部门办公厅（室）关于开展2025年“一起益企”中小企业服务行动的通知》要求，进一步提升我市中小企业国际化水平和核心竞争力，助力中小企业开拓国际市场、增强风险防控能力，天津市中小企业服务中心于近日成功举办轻工产业链中小企业出海对接交流会。中国中小企业发展促进中心、天津市贸促会、天津市食品工业协会、天津市自行车电动车行业协会、武清区崔黄口镇商会，以及来自自行车、食品、地毯等领域的生产制造企业和进出口企业等20余家单位代表参加了活动。  会上，国家中心产业集群发展研究所负责同志围绕中小企业“走出去”万帆耘海行动的实施背景、主要任务及具体进展作了专题报告，深入解读了当前中小企业出海的趋势、政策支持及发展机遇，为参会企业提供了权威的政策指导和行动路径。市贸促会会展服务中心相关负责同志重点介绍了贸促会组织的境外展会项目及我市境外参展补贴政策，并结合热点国家的贸易环境、市场动态及政策导向，为企业提供了详实的出海信息及参展支持服务，助力企业精准把握国际市场机遇。活动主题引起与会企业的高度关注，现场交流、对接气氛热烈。近年来，在国家大力推进构建以国内大循环为主体、国内国际双循环相互促进的新发展格局背景下，越来越多的中小企业将海外业务视为推动企业增长的重要战略。但在全球化经营过程中，中小企业面临诸多挑战，由于缺乏出海的实战经验，境外抗风险能力较低，减缓了我市企业出海的步伐。市中小企业服务中心2024年起已着手为有出海需求的企业搭建资源共享、经验互通的平台；2025年中心启动的“中小企业出海系列活动”涵盖出海对接交流会、国际市场调研、境外展会参展支持等多元化服务。下一步，中心将聚焦中小企业出海需求，深化与各行业协会及专业服务机构的协同合作，完善中小企业出海服务体系，整合资源系统化提供政策解读、市场开拓、国际人才引进、管理提升、跨境金融、权益保护、数字化转型及检测认证等关键节点的服务对接，助力企业提升国际化能力。推动我市中小企业在全球市场中形成竞争新优势。\n",
      "\n",
      "Source: {'source': 'https://gyxxh.tj.gov.cn/glllm/gabsycs/gxdtgh/202504/t20250408_6902492.html'}\n",
      "Content: 工信动态\n",
      "          \n",
      "\n",
      "\n",
      "\n",
      "\n",
      "\n",
      "        天津市中小企业服务中心成功举办轻工 产业链中小企业出海对接交流会\n",
      "      \n",
      "\n",
      "来源：\n",
      "          天津市工业和信息化局\n",
      "        \n",
      "发布时间：\n",
      "          2025-04-08 09:30\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "共有20余家企业参加活动，包括生产制造企业和进出口企业等。\n"
     ]
    }
   ],
   "source": [
    "input_message = \"有多少家企业参加活动?\"\n",
    "\n",
    "for step in graph.stream(\n",
    "    {\"messages\": [{\"role\": \"user\", \"content\": input_message}]},\n",
    "    stream_mode=\"values\",\n",
    "):\n",
    "    step[\"messages\"][-1].pretty_print()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bc3da923-4b3f-4084-b4f4-7ed6d9372207",
   "metadata": {},
   "source": [
    "### 聊天历史的状态化管理（Stateful Management of Chat History）\n",
    "\n",
    "\n",
    "\n",
    "### 在生产环境中\n",
    "\n",
    "在一个问答（Q&A）应用程序中，通常需要将聊天历史**持久化到数据库中**，并能够在后续交互中正确地读取和更新它。\n",
    "\n",
    "**LangGraph 提供了一个内置的持久化层（persistence layer）**，这使得它非常适合用于支持多轮对话的聊天应用。\n",
    "\n",
    "---\n",
    "\n",
    "### 如何实现？\n",
    "\n",
    "要支持多个对话轮次（turns）或多条对话线程（threads），我们只需在编译应用程序时指定一个 **checkpointer（检查点器）**。由于我们图中的各个节点都在向状态（state）中追加消息，因此我们可以在多次调用之间保留一致的聊天历史记录。\n",
    "\n",
    "LangGraph 自带了一个简单的**内存型 checkpointer**，我们在下面的示例中使用它。你也可以参考其[官方文档](https://example.com/checkpointer-docs)，了解更多关于使用其他持久化后端（如 SQLite 或 PostgreSQL）的方法。\n",
    "\n",
    "---\n",
    "\n",
    "### 示例代码（使用内存型 Checkpointer）\n",
    "\n",
    "```python\n",
    "from langgraph.checkpoint.memory import MemorySaver\n",
    "from langgraph.graph import StateGraph\n",
    "\n",
    "# 创建一个内存保存器\n",
    "checkpointer = MemorySaver()\n",
    "\n",
    "# 编译图并启用持久化\n",
    "app = graph.compile(checkpointer=checkpointer)\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "87359297-9b8b-4438-8e4d-123121e9f4b9",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langgraph.checkpoint.memory import MemorySaver\n",
    "\n",
    "memory = MemorySaver()\n",
    "graph = graph_builder.compile(checkpointer=memory)\n",
    "\n",
    "# Specify an ID for the thread\n",
    "config = {\"configurable\": {\"thread_id\": \"abc123\"}}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "8f3da989-951c-4a00-80b7-517bbc1b60ca",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "================================\u001b[1m Human Message \u001b[0m=================================\n",
      "\n",
      "文章发布的时间?\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "Tool Calls:\n",
      "  retrieve (call_7d018350d2144375952f55call_7d018350d2144375952f55call_7d018350d2144375952f55call_7d018350d2144375952f55)\n",
      " Call ID: call_7d018350d2144375952f55call_7d018350d2144375952f55call_7d018350d2144375952f55call_7d018350d2144375952f55\n",
      "  Args:\n",
      "    query: 发布日期\n",
      "=================================\u001b[1m Tool Message \u001b[0m=================================\n",
      "Name: retrieve\n",
      "\n",
      "Source: {'source': 'https://gyxxh.tj.gov.cn/glllm/gabsycs/gxdtgh/202504/t20250408_6902492.html'}\n",
      "Content: 工信动态\n",
      "          \n",
      "\n",
      "\n",
      "\n",
      "\n",
      "\n",
      "        天津市中小企业服务中心成功举办轻工 产业链中小企业出海对接交流会\n",
      "      \n",
      "\n",
      "来源：\n",
      "          天津市工业和信息化局\n",
      "        \n",
      "发布时间：\n",
      "          2025-04-08 09:30\n",
      "\n",
      "Source: {'source': 'https://gyxxh.tj.gov.cn/glllm/gabsycs/gxdtgh/202504/t20250408_6902492.html'}\n",
      "Content: 来源：\n",
      "          天津市工业和信息化局\n",
      "        \n",
      "发布时间：\n",
      "          2025-04-08 09:30\n",
      "        \n",
      "\n",
      "\n",
      "为深入贯彻党的二十届三中全会精神，落实《工业和信息化部办公厅关于开展中小企业出海服务专项行动的通知》及《工业和信息化部等17部门办公厅（室）关于开展2025年“一起益企”中小企业服务行动的通知》要求，进一步提升我市中小企业国际化水平和核心竞争力，助力中小企业开拓国际市场、增强风险防控能力，天津市中小企业服务中心于近日成功举办轻工产业链中小企业出海对接交流会。中国中小企业发展促进中心、天津市贸促会、天津市食品工业协会、天津市自行车电动车行业协会、武清区崔黄口镇商会，以及来自自行车、食品、地毯等领域的生产制造企业和进出口企业等20余家单位代表参加了活动。  会上，国家中心产业集群发展研究所负责同志围绕中小企业“走出去”万帆耘海行动的实施背景、主要任务及具体进展作了专题报告，深入解读了当前中小企业出海的趋势、政策支持及发展机遇，为参会企业提供了权威的政策指导和行动路径。市贸促会会展服务中心相关负责同志重点介绍了贸促会组织的境外展会项目及我市境外参展补贴政策，并结合热点国家的贸易环境、市场动态及政策导向，为企业提供了详实的出海信息及参展支持服务，助力企业精准把握国际市场机遇。活动主题引起与会企业的高度关注，现场交流、对接气氛热烈。近年来，在国家大力推进构建以国内大循环为主体、国内国际双循环相互促进的新发展格局背景下，越来越多的中小企业将海外业务视为推动企业增长的重要战略。但在全球化经营过程中，中小企业面临诸多挑战，由于缺乏出海的实战经验，境外抗风险能力较低，减缓了我市企业出海的步伐。市中小企业服务中心2024年起已着手为有出海需求的企业搭建资源共享、经验互通的平台；2025年中心启动的“中小企业出海系列活动”涵盖出海对接交流会、国际市场调研、境外展会参展支持等多元化服务。下一步，中心将聚焦中小企业出海需求，深化与各行业协会及专业服务机构的协同合作，完善中小企业出海服务体系，整合资源系统化提供政策解读、市场开拓、国际人才引进、管理提升、跨境金融、权益保护、数字化转型及检测认证等关键节点的服务对接，助力企业提升国际化能力。推动我市中小企业在全球市场中形成竞争新优势。\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "文章发布时间是2025年4月8日09:30。\n"
     ]
    }
   ],
   "source": [
    "input_message = \"文章发布的时间?\"\n",
    "\n",
    "for step in graph.stream(\n",
    "    {\"messages\": [{\"role\": \"user\", \"content\": input_message}]},\n",
    "    stream_mode=\"values\",\n",
    "    config=config,\n",
    "):\n",
    "    step[\"messages\"][-1].pretty_print()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "3141a4a2-2297-46d4-8b26-17a92214209f",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "================================\u001b[1m Human Message \u001b[0m=================================\n",
      "\n",
      "“走出去”的口号是什么意思?\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "Tool Calls:\n",
      "  retrieve (call_49e867ccad2d4e2f8a2144call_49e867ccad2d4e2f8a2144call_49e867ccad2d4e2f8a2144call_49e867ccad2d4e2f8a2144)\n",
      " Call ID: call_49e867ccad2d4e2f8a2144call_49e867ccad2d4e2f8a2144call_49e867ccad2d4e2f8a2144call_49e867ccad2d4e2f8a2144\n",
      "  Args:\n",
      "    query: 走出去的含义\n",
      "=================================\u001b[1m Tool Message \u001b[0m=================================\n",
      "Name: retrieve\n",
      "\n",
      "Source: {'source': 'https://gyxxh.tj.gov.cn/glllm/gabsycs/gxdtgh/202504/t20250408_6902492.html'}\n",
      "Content: 来源：\n",
      "          天津市工业和信息化局\n",
      "        \n",
      "发布时间：\n",
      "          2025-04-08 09:30\n",
      "        \n",
      "\n",
      "\n",
      "为深入贯彻党的二十届三中全会精神，落实《工业和信息化部办公厅关于开展中小企业出海服务专项行动的通知》及《工业和信息化部等17部门办公厅（室）关于开展2025年“一起益企”中小企业服务行动的通知》要求，进一步提升我市中小企业国际化水平和核心竞争力，助力中小企业开拓国际市场、增强风险防控能力，天津市中小企业服务中心于近日成功举办轻工产业链中小企业出海对接交流会。中国中小企业发展促进中心、天津市贸促会、天津市食品工业协会、天津市自行车电动车行业协会、武清区崔黄口镇商会，以及来自自行车、食品、地毯等领域的生产制造企业和进出口企业等20余家单位代表参加了活动。  会上，国家中心产业集群发展研究所负责同志围绕中小企业“走出去”万帆耘海行动的实施背景、主要任务及具体进展作了专题报告，深入解读了当前中小企业出海的趋势、政策支持及发展机遇，为参会企业提供了权威的政策指导和行动路径。市贸促会会展服务中心相关负责同志重点介绍了贸促会组织的境外展会项目及我市境外参展补贴政策，并结合热点国家的贸易环境、市场动态及政策导向，为企业提供了详实的出海信息及参展支持服务，助力企业精准把握国际市场机遇。活动主题引起与会企业的高度关注，现场交流、对接气氛热烈。近年来，在国家大力推进构建以国内大循环为主体、国内国际双循环相互促进的新发展格局背景下，越来越多的中小企业将海外业务视为推动企业增长的重要战略。但在全球化经营过程中，中小企业面临诸多挑战，由于缺乏出海的实战经验，境外抗风险能力较低，减缓了我市企业出海的步伐。市中小企业服务中心2024年起已着手为有出海需求的企业搭建资源共享、经验互通的平台；2025年中心启动的“中小企业出海系列活动”涵盖出海对接交流会、国际市场调研、境外展会参展支持等多元化服务。下一步，中心将聚焦中小企业出海需求，深化与各行业协会及专业服务机构的协同合作，完善中小企业出海服务体系，整合资源系统化提供政策解读、市场开拓、国际人才引进、管理提升、跨境金融、权益保护、数字化转型及检测认证等关键节点的服务对接，助力企业提升国际化能力。推动我市中小企业在全球市场中形成竞争新优势。\n",
      "\n",
      "Source: {'source': 'https://gyxxh.tj.gov.cn/glllm/gabsycs/gxdtgh/202504/t20250408_6902492.html'}\n",
      "Content: 工信动态\n",
      "          \n",
      "\n",
      "\n",
      "\n",
      "\n",
      "\n",
      "        天津市中小企业服务中心成功举办轻工 产业链中小企业出海对接交流会\n",
      "      \n",
      "\n",
      "来源：\n",
      "          天津市工业和信息化局\n",
      "        \n",
      "发布时间：\n",
      "          2025-04-08 09:30\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "“走出去”指的是鼓励中小企业拓展海外市场，提升国际化水平和竞争力。具体内容包括提供政策指导、市场开拓、国际人才引进等服务，帮助企业增强境外抗风险能力。\n"
     ]
    }
   ],
   "source": [
    "input_message = \"“走出去”的口号是什么意思?\"\n",
    "\n",
    "for step in graph.stream(\n",
    "    {\"messages\": [{\"role\": \"user\", \"content\": input_message}]},\n",
    "    stream_mode=\"values\",\n",
    "    config=config,\n",
    "):\n",
    "    step[\"messages\"][-1].pretty_print()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "0a89e006-bcea-4fd2-b91a-d23a67f7bfd6",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "================================\u001b[1m Human Message \u001b[0m=================================\n",
      "\n",
      "是星期几呢?\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "Tool Calls:\n",
      "  retrieve (call_d7193225ab704fa0b1726ccall_d7193225ab704fa0b1726ccall_d7193225ab704fa0b1726ccall_d7193225ab704fa0b1726ccall_d7193225ab704fa0b1726ccall_d7193225ab704fa0b1726c)\n",
      " Call ID: call_d7193225ab704fa0b1726ccall_d7193225ab704fa0b1726ccall_d7193225ab704fa0b1726ccall_d7193225ab704fa0b1726ccall_d7193225ab704fa0b1726ccall_d7193225ab704fa0b1726c\n",
      "  Args:\n",
      "    query: 2025年4月8日是星期几\n",
      "=================================\u001b[1m Tool Message \u001b[0m=================================\n",
      "Name: retrieve\n",
      "\n",
      "Source: {'source': 'https://gyxxh.tj.gov.cn/glllm/gabsycs/gxdtgh/202504/t20250408_6902492.html'}\n",
      "Content: 来源：\n",
      "          天津市工业和信息化局\n",
      "        \n",
      "发布时间：\n",
      "          2025-04-08 09:30\n",
      "        \n",
      "\n",
      "\n",
      "为深入贯彻党的二十届三中全会精神，落实《工业和信息化部办公厅关于开展中小企业出海服务专项行动的通知》及《工业和信息化部等17部门办公厅（室）关于开展2025年“一起益企”中小企业服务行动的通知》要求，进一步提升我市中小企业国际化水平和核心竞争力，助力中小企业开拓国际市场、增强风险防控能力，天津市中小企业服务中心于近日成功举办轻工产业链中小企业出海对接交流会。中国中小企业发展促进中心、天津市贸促会、天津市食品工业协会、天津市自行车电动车行业协会、武清区崔黄口镇商会，以及来自自行车、食品、地毯等领域的生产制造企业和进出口企业等20余家单位代表参加了活动。  会上，国家中心产业集群发展研究所负责同志围绕中小企业“走出去”万帆耘海行动的实施背景、主要任务及具体进展作了专题报告，深入解读了当前中小企业出海的趋势、政策支持及发展机遇，为参会企业提供了权威的政策指导和行动路径。市贸促会会展服务中心相关负责同志重点介绍了贸促会组织的境外展会项目及我市境外参展补贴政策，并结合热点国家的贸易环境、市场动态及政策导向，为企业提供了详实的出海信息及参展支持服务，助力企业精准把握国际市场机遇。活动主题引起与会企业的高度关注，现场交流、对接气氛热烈。近年来，在国家大力推进构建以国内大循环为主体、国内国际双循环相互促进的新发展格局背景下，越来越多的中小企业将海外业务视为推动企业增长的重要战略。但在全球化经营过程中，中小企业面临诸多挑战，由于缺乏出海的实战经验，境外抗风险能力较低，减缓了我市企业出海的步伐。市中小企业服务中心2024年起已着手为有出海需求的企业搭建资源共享、经验互通的平台；2025年中心启动的“中小企业出海系列活动”涵盖出海对接交流会、国际市场调研、境外展会参展支持等多元化服务。下一步，中心将聚焦中小企业出海需求，深化与各行业协会及专业服务机构的协同合作，完善中小企业出海服务体系，整合资源系统化提供政策解读、市场开拓、国际人才引进、管理提升、跨境金融、权益保护、数字化转型及检测认证等关键节点的服务对接，助力企业提升国际化能力。推动我市中小企业在全球市场中形成竞争新优势。\n",
      "\n",
      "Source: {'source': 'https://gyxxh.tj.gov.cn/glllm/gabsycs/gxdtgh/202504/t20250408_6902492.html'}\n",
      "Content: 工信动态\n",
      "          \n",
      "\n",
      "\n",
      "\n",
      "\n",
      "\n",
      "        天津市中小企业服务中心成功举办轻工 产业链中小企业出海对接交流会\n",
      "      \n",
      "\n",
      "来源：\n",
      "          天津市工业和信息化局\n",
      "        \n",
      "发布时间：\n",
      "          2025-04-08 09:30\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "文章发布的日期是2025年4月8日，是周二。\n"
     ]
    }
   ],
   "source": [
    "input_message = \"是星期几呢?\"\n",
    "\n",
    "for step in graph.stream(\n",
    "    {\"messages\": [{\"role\": \"user\", \"content\": input_message}]},\n",
    "    stream_mode=\"values\",\n",
    "    config=config,\n",
    "):\n",
    "    step[\"messages\"][-1].pretty_print()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d81d75c2-0c92-4f94-a577-0f6f14008efa",
   "metadata": {},
   "source": [
    "> 请注意，问题中模型生成的查询包含了对话上下文。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b7f681ec-bdcd-47bd-8b21-af94956fe16b",
   "metadata": {},
   "source": [
    "### 智能体（Agents）\n",
    "\n",
    "**智能体（Agents）** 利用大语言模型（LLMs）的推理能力，在执行过程中进行决策。使用智能体可以让你在检索流程中拥有更多的自主判断空间。\n",
    "\n",
    "虽然相比前面提到的“链式调用（chain）”方式，智能体的行为更具不确定性、更难预测，但它们能够在处理一个查询时：\n",
    "\n",
    "- 执行**多个检索步骤**；\n",
    "- 或者对一次检索结果进行**迭代优化**。\n",
    "\n",
    "---\n",
    "\n",
    "### 构建一个最简 RAG 智能体\n",
    "\n",
    "下面我们构建一个最小的 RAG 智能体。借助 LangGraph 提供的预构建 **ReAct 智能体构造器（agent constructor）**，我们只需一行代码即可完成创建：\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "92ceaccd-f50d-49a9-a548-802515de1dae",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langgraph.prebuilt import create_react_agent\n",
    "\n",
    "agent_executor = create_react_agent(llm, [retrieve], checkpointer=memory)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "df9e9353-0611-41fa-a94b-4d59febe464f",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "display(Image(agent_executor.get_graph().draw_mermaid_png()))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "01e0da66-07b2-44b7-afa7-bc9cd3ad5993",
   "metadata": {},
   "source": [
    "与我们之前实现的最大区别在于：  \n",
    "以前的流程中，最后一步是生成答案并结束整个流程；而在智能体（Agent）模式中，**工具调用会回传给原始的语言模型调用**，形成一个循环。\n",
    "\n",
    "这样一来，模型可以根据检索到的信息：\n",
    "\n",
    "- 直接回答用户的问题；\n",
    "- 或者再次生成一个新的工具调用，以获取更多信息。\n",
    "\n",
    "这种机制使得模型能够在需要时进行多轮检索，从而逐步逼近最终答案。\n",
    "\n",
    "---\n",
    "\n",
    "### 我们来测试一下这个功能\n",
    "\n",
    "我们可以构造一个通常需要**多次检索步骤**才能回答的问题。例如：\n",
    "\n",
    "> “这是星期几发布的？”\n",
    "\n",
    "这个问题包含多个层次。\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "a36fdcbd-13b4-4cb1-86f3-430377ea121b",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "================================\u001b[1m Human Message \u001b[0m=================================\n",
      "\n",
      "这是星期几发布的？\n",
      "\n",
      "得到答案后，查找该方法的常见扩展。\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "Tool Calls:\n",
      "  retrieve (call_77ef58d390ee4b1b8454f6call_77ef58d390ee4b1b8454f6call_77ef58d390ee4b1b8454f6call_77ef58d390ee4b1b8454f6)\n",
      " Call ID: call_77ef58d390ee4b1b8454f6call_77ef58d390ee4b1b8454f6call_77ef58d390ee4b1b8454f6call_77ef58d390ee4b1b8454f6\n",
      "  Args:\n",
      "    query: 发布日期 星期几\n",
      "=================================\u001b[1m Tool Message \u001b[0m=================================\n",
      "Name: retrieve\n",
      "\n",
      "Source: {'source': 'https://gyxxh.tj.gov.cn/glllm/gabsycs/gxdtgh/202504/t20250408_6902492.html'}\n",
      "Content: 工信动态\n",
      "          \n",
      "\n",
      "\n",
      "\n",
      "\n",
      "\n",
      "        天津市中小企业服务中心成功举办轻工 产业链中小企业出海对接交流会\n",
      "      \n",
      "\n",
      "来源：\n",
      "          天津市工业和信息化局\n",
      "        \n",
      "发布时间：\n",
      "          2025-04-08 09:30\n",
      "\n",
      "Source: {'source': 'https://gyxxh.tj.gov.cn/glllm/gabsycs/gxdtgh/202504/t20250408_6902492.html'}\n",
      "Content: 来源：\n",
      "          天津市工业和信息化局\n",
      "        \n",
      "发布时间：\n",
      "          2025-04-08 09:30\n",
      "        \n",
      "\n",
      "\n",
      "为深入贯彻党的二十届三中全会精神，落实《工业和信息化部办公厅关于开展中小企业出海服务专项行动的通知》及《工业和信息化部等17部门办公厅（室）关于开展2025年“一起益企”中小企业服务行动的通知》要求，进一步提升我市中小企业国际化水平和核心竞争力，助力中小企业开拓国际市场、增强风险防控能力，天津市中小企业服务中心于近日成功举办轻工产业链中小企业出海对接交流会。中国中小企业发展促进中心、天津市贸促会、天津市食品工业协会、天津市自行车电动车行业协会、武清区崔黄口镇商会，以及来自自行车、食品、地毯等领域的生产制造企业和进出口企业等20余家单位代表参加了活动。  会上，国家中心产业集群发展研究所负责同志围绕中小企业“走出去”万帆耘海行动的实施背景、主要任务及具体进展作了专题报告，深入解读了当前中小企业出海的趋势、政策支持及发展机遇，为参会企业提供了权威的政策指导和行动路径。市贸促会会展服务中心相关负责同志重点介绍了贸促会组织的境外展会项目及我市境外参展补贴政策，并结合热点国家的贸易环境、市场动态及政策导向，为企业提供了详实的出海信息及参展支持服务，助力企业精准把握国际市场机遇。活动主题引起与会企业的高度关注，现场交流、对接气氛热烈。近年来，在国家大力推进构建以国内大循环为主体、国内国际双循环相互促进的新发展格局背景下，越来越多的中小企业将海外业务视为推动企业增长的重要战略。但在全球化经营过程中，中小企业面临诸多挑战，由于缺乏出海的实战经验，境外抗风险能力较低，减缓了我市企业出海的步伐。市中小企业服务中心2024年起已着手为有出海需求的企业搭建资源共享、经验互通的平台；2025年中心启动的“中小企业出海系列活动”涵盖出海对接交流会、国际市场调研、境外展会参展支持等多元化服务。下一步，中心将聚焦中小企业出海需求，深化与各行业协会及专业服务机构的协同合作，完善中小企业出海服务体系，整合资源系统化提供政策解读、市场开拓、国际人才引进、管理提升、跨境金融、权益保护、数字化转型及检测认证等关键节点的服务对接，助力企业提升国际化能力。推动我市中小企业在全球市场中形成竞争新优势。\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "该信息是在2025年4月8日发布的，这一天是星期二。\n",
      "\n",
      "关于您提到的方法的常见扩展包括：\n",
      "\n",
      "1. 使用自然语言处理技术来自动识别文档中的日期和星期信息。\n",
      "2. 在文档中加入更多的元数据，以便更容易地跟踪和检索。\n",
      "3. 将日期和星期信息与其他相关信息（如地理位置、事件类型等）关联起来，以提供更全面的信息视图。\n",
      "\n",
      "请注意，这些扩展可能会根据具体的使用场景和技术条件有所不同。\n"
     ]
    }
   ],
   "source": [
    "config = {\"configurable\": {\"thread_id\": \"def234\"}}\n",
    "\n",
    "input_message = (\n",
    "    \"这是星期几发布的？\\n\\n\"\n",
    "    \"得到答案后，查找该方法的常见扩展。\"\n",
    ")\n",
    "\n",
    "for event in agent_executor.stream(\n",
    "    {\"messages\": [{\"role\": \"user\", \"content\": input_message}]},\n",
    "    stream_mode=\"values\",\n",
    "    config=config,\n",
    "):\n",
    "    event[\"messages\"][-1].pretty_print()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0e1d0c51-4d81-47d5-afad-a5f480096ce9",
   "metadata": {},
   "source": [
    "### 请注意，智能体（Agent）的执行过程如下：\n",
    "\n",
    "1. **生成一个查询**，用于搜索任务分解的标准方法；\n",
    "2. 在**收到第一轮检索结果后**，生成**第二个查询**，用于搜索该方法的常见扩展或补充内容；\n",
    "3. 在**获取到所有必要的上下文信息后**，最终生成对用户问题的完整回答。\n",
    "\n",
    "这种多步骤、自动化的检索流程使得智能体能够更深入地理解并回答复杂问题。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0b6bc7de-9ebb-43a6-90d8-241dc28e13b9",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.13"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
